package top.superflow.db.orientdb

import com.orientechnologies.orient.core.db.{ODatabaseType, OrientDB, OrientDBConfig}
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx
import com.orientechnologies.orient.server.{OServer, OServerMain}
import top.superflow.constant.GlobalConst
import top.superflow.db.api._
import top.superflow.exception.{DBServerErrorCode, DBServerErrorMsg, DBServerException}
import top.superflow.log.Loggable
import top.superflow.utils.{ClasspathUtil, SuperflowENVUtil}
import top.superflow.verify.Ensure

import scala.collection.immutable.Map
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Future, Promise}

class OrientDBServer private extends DBServer
                      with Loggable
                      with Ensure
                      with DBConfigurationValidator{
  
  private val server : OServer = OServerMain.create()
  
  private var status : DBStatus = DBStatus.SHUTDOWN
  private var activeResult : Future[DBServer] = _
  private val databases : Map[String, DBConnection] = Map()
  
  private var dbPool : DBPool = _ 
  
  private var serverConfig : OrientServerConfiguration = _
  
  
  
  
  
  def getStatus : DBStatus = {
    if(this.server.isActive()){
      return DBStatus.RUNNING
    }
    this.status
  }  
  
  def shutdown: Future[DBServer] = {
    val p = Promise[DBServer]()
    Future {
      logger.info("Go to shut down the Orient DB server")
      server.shutdown()
      this.setStatus(DBStatus.SHUTDOWN)
      p success this
    }
    p.future
  }
  
  def active : Future[DBServer] = {
    val p = Promise[DBServer]()
    Future {
      logger.info("Go to active the Orient DB server")
      server.activate()
      this.setStatus(DBStatus.RUNNING)
      this.addRootUser
      p success this
    }
    p.future
  }
  
  private def addRootUser = {
    this.server.getSecurity.reload(ClasspathUtil.retrieveRealPathFromClasspath(SuperflowENVUtil.getProperty(GlobalConst.SYSTEM_DB_CONFIG_SECURITY_CONFIG_PATH)))
  }
  
  def startup(dbConfiguration: DBServerConfiguration): Future[Future[DBServer]] = {
    val p = Promise[Future[DBServer]]()
    Future {
      dbConfiguration match {
        case x: OrientServerConfiguration => {
          this.serverConfig = x
          logger.info("Go to start up the Orient DB server")
          this.setStatus(DBStatus.STARTING)
          server.startup(x.getConfiguration)          
          this.activeResult = this.active
//          activeResult onComplete { 
//            dbServer => this.setStatus(DBStatus.RUNNING)
//          }
//          activeResult onFailure {
//            case e => logger.error("The orient DB is failed to be actived.", e)
//          }
          
          p success activeResult
        } 
        case _ => {
          throw new DBServerException(DBServerErrorCode.CONFIGURATION_UNSUPORTED, DBServerErrorMsg.CONFIGURATION_UNSUPORTED)
          p failure _
        }
      }
    }
    p.future
  }
  
  private[orientdb] def setStatus(status : DBStatus) = {
    this.status = status
  }
  
  def getDBHome : String = {
    SuperflowENVUtil.getAssuredProperty(GlobalConst.SYSTEM_ENV_VAR_DB_HOME)
  }
  
  def load(dbConfig : DBConfiguration): Future[DBPool] = {
    //http://www.javased.com/?api=com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx
    notNull(dbConfig, "Database configuration should not be null")
    
    val p = Promise[DBPool]()
    Future {
      logger.info("Make sure the DB exist")
      ensureExist(dbConfig)
      
      logger.info("Go to load database:{}" , dbConfig.getUrl)
      this.dbPool = new OrientDBPool(dbConfig)
      
      p success this.dbPool
    }
    p.future
    
  }
  
  private def ensureExist(dbConfig : DBConfiguration){
    create(dbConfig)
  }
  
  
  def create(dbConfig : DBConfiguration) : Unit= {
    validate(dbConfig)
    
    if(dbExists(dbConfig)) {
      logger.warn("The Database {} exists already.", dbConfig.getUrl);
      return
    }
    
    val db : OrientDB = new OrientDB(dbConfig.getUrl, dbConfig.getUsername, dbConfig.getPassword,OrientDBConfig.defaultConfig());
    db.create(dbConfig.getDBName, ODatabaseType.PLOCAL)
    db.close()
  }
  
  def dbExists (dbConfig : DBConfiguration) : Boolean = {
    validate(dbConfig)

    server.existsStoragePath(dbConfig.getUrl)
  }
  
  def register(database : DBConnection) = ???
  
  def addUser(user : String, password : String, resources : String) = {
    try {
      this.server.addUser(user, password, resources)
    } catch {
      case t: Throwable => t.printStackTrace() // TODO: handle error
    }
    
  }
  
  def dropUser(user : String, password : String) = {
    this.server.dropUser(user)
  }

}

object OrientDBServer {

  private val _instance = new OrientDBServer

  def getInstance: OrientDBServer = {
      _instance
  }
  
}